Skip to content

Fix eager-load key type coercion#274

Open
jbeers wants to merge 1 commit into
coldbox-modules:mainfrom
jbeers:fix/eager-load-key-binding-types
Open

Fix eager-load key type coercion#274
jbeers wants to merge 1 commit into
coldbox-modules:mainfrom
jbeers:fix/eager-load-key-binding-types

Conversation

@jbeers
Copy link
Copy Markdown

@jbeers jbeers commented May 13, 2026

Summary

This fixes eager-loading paths that were coercing numeric key values to strings before QB built the relationship query bindings.

The bug came from using toList() / listToArray() as both:

  • the dedupe representation for composite eager-load keys
  • the transport format for the actual key values

That round-trip converted numeric IDs like 1 into string values like "1", which then caused QB to bind them as varchar instead of integer.

Changes

  • preserve original eager-load key arrays in BaseRelationship.getKeys()
  • preserve original eager-load key arrays in BelongsTo.getEagerEntityKeys()
  • use a serialized lookup key only for deduplication
  • add regression coverage asserting integer binding types for eager-loaded belongsTo and belongsToMany

Validation

  • started MySQL locally using the repo README/CI settings
  • started the local Lucee 6 server with server-lucee@6.json
  • ran:
    box testbox run bundles=tests/specs/integration/BaseEntity/Relationships/EagerLoadingSpec.cfc
  • result: 29 passed, 0 failed, 0 errored

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes eager-loading key handling so numeric IDs remain numeric through key collection/deduplication, preventing QueryBuilder from binding them as string/varchar types during relationship eager loads.

Changes:

  • Update eager-load key collection/deduplication to preserve original key value types while still deduping composite keys.
  • Adjust BelongsTo.getEagerEntityKeys() and BaseRelationship.getKeys() to use a serialized representation only as a lookup key.
  • Add regression tests asserting integer binding types for eager-loaded belongsTo and belongsToMany relationships.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
models/Relationships/BaseRelationship.cfc Changes eager-load key collection to preserve original key array values and dedupe via serialized lookup keys.
models/Relationships/BelongsTo.cfc Updates eager-load entity key extraction to retain original key arrays and dedupe via serialized lookup keys.
tests/specs/integration/BaseEntity/Relationships/EagerLoadingSpec.cfc Adds regression coverage to ensure eager-load query bindings remain numeric (integer) rather than varchar.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

return isStruct( binding ) && ( binding.keyExists( "cfsqltype" ) || binding.keyExists( "sqltype" ) );
} )
.map( function( binding ) {
return lCase( binding.cfsqltype ?: binding.sqltype );
Comment on lines +362 to +374
var uniqueKeys = arguments.entities.reduce( function( acc, entity ) {
var keyValues = [];
for ( var key in keys ) {
var value = structKeyExists( entity, "isQuickEntity" ) ? entity.retrieveAttribute( key ) : entity[ key ];
if ( entityIsNullValue( baseEntity, key, value ) ) {
return acc;
}
acc.append( keyValues.toList() );
return acc;
}, [] )
).map( function( key ) {
return key.listToArray();
} );
keyValues.append( value );
}

acc[ serializeJSON( keyValues ) ] = keyValues;
return acc;
}, {} );
@elpete
Copy link
Copy Markdown
Collaborator

elpete commented May 13, 2026

@copilot Can you verify if the values used in the bindings are expanded to full struct bindings before being serialized as JSON to check for duplicate keys in the code path this PR is covering?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants